home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Aminet 50
/
Aminet 50 (2002)(GTI - Schatztruhe)[!][Aug 2002].iso
/
Aminet
/
disk
/
bakup
/
DSrestore.lha
/
DSrestore
/
DSrestore.c
< prev
next >
Wrap
C/C++ Source or Header
|
2002-06-13
|
19KB
|
528 lines
/* DSrestore.c */
/* Copyright 2001-2002 Oliver B. Warzecha <obw@amarok.ping.de> */
/*
This file is part of DSrestore.
DSrestore is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
DSrestore is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with DSrestore; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <proto/exec.h>
#include <proto/dos.h>
#include <exec/memory.h>
#include <dos/rdargs.h>
#include <ds_stream.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <dos.h>
#include "DSrestore.h"
#include "DSrestore_rev.h"
static const char* verID = VERSTAG;
/* defaults for development */
const char *fbtarget="Sys:";
static long debug;
struct RDArgs *rdArgs;
char *target,*backup;
char *buff;
int err;
void *mypool;
BPTR infile,tmplock,StdErr,logFile;
int main(int argc, char ** argv)
{
int rc=0;
logFile = 0;
StdErr=Open("CONSOLE:", MODE_OLDFILE);
/* here we parse the commandline */
mypool = CreatePool(MEMF_CLEAR,4096,STBLOCKSIZE);
if (mypool == NULL) {
FPrintf(StdErr, "unable to create memory pool");
Close(StdErr);
return 20;
}
read_params();
/*
* now we know the name of the archive and the directory to
* place the restored data in.
*/
if (!parse_stream(backup)) {
PrintFault(IoErr(), "DSrestore problem: ");
rc = 10;
}
/*
* we could do more here.
*/
if(buff)
FreeVec(buff);
FreeArgs(rdArgs);
FreeVec(target);
FreeVec(backup);
DeletePool(mypool);
Close(infile);
Close(StdErr);
return rc;
}
void read_params(void)
{
char *argStr = ARGSTRING;
LONG argArray[4];
LONG errc;
/* set defaults */
/* target = current directory */
buff = AllocVec(256, MEMF_CLEAR);
if (!(tmplock = Lock("PROGDIR:", ACCESS_READ)))
FPrintf(StdErr,"lock on current dir failed\n");
if (!NameFromLock(tmplock, buff, 255)) {
if ((errc = IoErr()) == ERROR_LINE_TOO_LONG)
FPrintf(StdErr, "Current directory name too long, using SYS: (this is probably not what you want)\n");
else {
PrintFault(errc, "Error on current dir");
}
FreeVec(buff);
buff = NULL;
argArray[1] = (LONG)fbtarget;
} else
argArray[1] = (LONG)buff;
UnLock(tmplock);
argArray[2] = 0;
if (rdArgs = ReadArgs(argStr, argArray, NULL)) {
debug = argArray[2];
target = AllocVec(strlen((char *)argArray[1])+1UL, MEMF_CLEAR);
strcpy(target, (char *)argArray[1]);
backup = AllocVec(strlen((char *)argArray[0])+1UL, MEMF_CLEAR);
strcpy(backup, (char *)argArray[0]);
} else {
FPrintf(StdErr,"required argument missing\n");
if (buff)
FreeVec(buff);
FreeArgs(rdArgs);
rdArgs = NULL;
Close(StdErr);
DeletePool(mypool);
SetIoErr(ERROR_REQUIRED_ARG_MISSING);
exit(20);
}
if (argArray[3]) {
logFile = Open((STRPTR)argArray[3], MODE_NEWFILE);
if (!logFile) {
FPrintf(StdErr, "couldn't open file %s for logging, continuing\n", argArray[3]);
}
}
FreeArgs(rdArgs);
rdArgs = NULL;
return;
}
/*
* readnext
* read a block from the stream in the supplied buffer
*/
void readnext(BPTR infile, void* buffer)
{
LONG size;
size = Read(infile, buffer, STBLOCKSIZE);
/*
* sophisticated error handling. Is the stream padded?
*/
if (size != STBLOCKSIZE)
FPrintf(StdErr,"Short read at file position %ld", Seek(infile, 0, OFFSET_CURRENT));
return;
}
/*
* API for ID->Array conversion, put in another file?
*/
static long *IDarr=NULL;
static long maxid=0;
/* constructor */
BOOL ID__ID(long nid)
{
if (!(IDarr =(long *)AllocPooled(mypool, (ULONG)((size_t)nid * sizeof (long)))))
return FALSE;
maxid = nid;
return TRUE;
}
/* destructor */
BOOL IDn_ID(void)
{
if (IDarr == NULL)
return FALSE;
/* free the whole array */
maxid = 0;
FreePooled(mypool, IDarr, (ULONG)(maxid * sizeof(long)));
return TRUE;
}
long indexID(long id)
{
long sc=0;
while (IDarr[sc] != id && sc != maxid)
sc++;
if (maxid == sc) {
FPrintf(StdErr,"object with ID %ld not found.\n", id);
return -1; /* sign for error */
}
if (debug)
Printf("searching index for ID %ld, found at %ld\n",id,sc);
return sc;
}
long getID(long index)
{
return IDarr[index];
}
BOOL setID(long id, long index)
{
if (index >= maxid)
return FALSE;
if (debug)
Printf("setting index %ld to ID %ld\n",index, id);
IDarr[index] = id;
return TRUE;
}
/*
* we have the name of the stream to parse
* Make it so!
*/
BOOL parse_stream(const char* name)
{
void *rootbuffer;
void *block=NULL;
STRoot* BRoot;
STHeader* BHeader;
char **Pathlist;
STBaseUnit *counters;
short fixbug = 0;
int NPaths;
/* open the file, non existing error handling again */
if (!(infile = Open((STRPTR)name, MODE_OLDFILE))) {
FPrintf(StdErr,"Unable to open archive file %s\n",(LONG)name);
return FALSE;
}
/* there _should_ be enough memory, doesn't it? */
rootbuffer = AllocPooled(mypool, STBLOCKSIZE);
counters = AllocPooled(mypool, sizeof(STBaseUnit));
/*
* The file is open. Read the first block and check, if it is really
* a disksalv stream and not a jpg or anything.
* so we do a consistency check!
*/
readnext(infile, rootbuffer);
BRoot = (STRoot*)rootbuffer;
/*
* this is the number of objects! We need to store this in a safe area.
* best if we leave the rootblock alone for the duration of the process.
* we also need a counter to count the objects for ourselves.
*/
if (debug)
Printf("%ld Objekte im Archiv\n",BRoot->barcount);
if ((BRoot->head.type != ID_ROOT) || (BRoot->head.id != 0)) {
FPrintf(StdErr,"no rootblock or unknown archive format!\nexiting...\n");
SetIoErr(ERROR_OBJECT_WRONG_TYPE);
return FALSE;
}
/* now the business starts */
/*
* we loop through the whole archive until the last block is read.
* last block is recognized by ID_ENDA. Every object is identified by
* the unique(!) ID number. So we have to store it somehow to handle links,
* subdirs, etc. we need the ID and the full path. sounds like a stringarray to
* me. and we know the size, too! ID does not start with zero, seems to
* be the disk key. :-( storage Methods: utility namespace objects?
* No, just a second array. I have to provide a small API, see above for
* #?ID()-Functions.
*/
if (!ID__ID(BRoot->barcount))
FPrintf(StdErr,"failed to allocate ID array\n");
/*
* Oh, hazy, what have you done! Objects in the root directory have a parent
* ID containing the block number of the root block! But the object containing
* this ID is of course _not_ in the archive stream. So I have to assume that the first
* object in the archive is in the root dir. sigh!
*/
NPaths = (size_t)BRoot->barcount * sizeof(char *);
if ((Pathlist = (char**)AllocPooled(mypool, (ULONG)NPaths)) == NULL)
FPrintf(StdErr,"Allocation for object array failed\n");
if (!(tmplock = Lock((STRPTR)target, ACCESS_READ))) {
FPrintf(StdErr,"Unable to find target dir %s\n",(LONG)target);
return FALSE;
}
Pathlist[0] = (char *)AllocPooled(mypool, 255 * sizeof(char));
/* 255 should be the maximum length of a BSTR (volumename) */
if (!(NameFromLock(tmplock, (STRPTR)Pathlist[0], 254))) {
FPrintf(StdErr,"could not get path for target dir %s, going downhill...\n", (LONG)target);
return FALSE;
}
UnLock(tmplock);
/* we give the root dir an ID of 0 first */
if (!setID(0,0)) {
return FALSE; /* the allocation above must have failed */
}
counters->objectcount = 0;
do {
if (block)
FreePooled(mypool, block, STBLOCKSIZE);
if (CheckSignal(SIGBREAKF_CTRL_C)) {
SetIoErr(ERROR_BREAK);
return FALSE;
}
block=AllocPooled(mypool, STBLOCKSIZE);
readnext(infile, block);
BHeader = (STHeader *)block;
if (BHeader->type == ID_DATA || BHeader->type == ID_ENDA)
continue;
/* store ID */
/*
* yeeha! broken DiskSalv (?)! the current ID can be 0. there is it. :-P
*/
if (fixbug == 1) {
if (BHeader->type == ID_UDIR && BHeader->parent == counters->dwalloc) {
/* last one was an directory, both have the same parent -> last dir was empty.
* so the ID of the last dir is unimportant, set it to 0. */
setID(0,counters->objectcount);
} else {
setID(BHeader->parent,counters->objectcount);
FPrintf(StdErr,"Bugfix: set ID of object no. %ld to %ld\n",counters->objectcount,BHeader->parent);
}
fixbug = 0;
}
if (BHeader->id == 0){
fixbug = 1;
counters->dwalloc = BHeader->parent;
}
counters->objectcount++;
if (!setID(BHeader->id,counters->objectcount))
FPrintf(StdErr,"ID storage for %ld failed", (LONG)BHeader->id);
/*
* TODO: check, if ID already allocated
* more consistency checks at all
* clean up code
*/
/*
* special case for 1st object.
* fetch parent ID and store as ID for root
*/
if (1 == counters->objectcount) {
setID(BHeader->parent,0);
}
/* the parent should already have been handled, get lock for parent name */
if (debug)
Printf("parent ID: %ld", BHeader->parent);
if (Pathlist[indexID(BHeader->parent)] == NULL) {
long i;
PutStr("parent not yet initialised, exiting\n");
for (i=0; Pathlist[i] != NULL; i++)
FPrintf(StdErr,"Pathlist[%ld]=%s\n",i,Pathlist[i]);
SetIoErr(ERROR_DIR_NOT_FOUND);
return FALSE;
}
if (debug)
Printf(
"current object: %s, parent: %s\n",
(LONG)((STDir*)BHeader)->body.filename,
Pathlist[indexID(BHeader->parent)] );
counters->dwsize = strlen(Pathlist[indexID(BHeader->parent)]);
if (debug)
Printf("first path component length: %ld, current filename %s\n",
counters->dwsize,
((STDir*)BHeader)->body.filename );
/* space for filenames + \0 + separator */
counters->dwsize += (strlen(((STDir*)BHeader)->body.filename)+2);
/* counters->out as temp pointer from STBaseUnit */
if (debug)
Printf("Allocating %ld bytes\n", counters->dwsize);
counters->out = AllocPooled(mypool, counters->dwsize);
Pathlist[counters->objectcount] = counters->out;
strcpy(counters->out, Pathlist[indexID(BHeader->parent)]);
/* counters->out is the path of the object */
if (!(AddPart(counters->out,((STDir*)BHeader)->body.filename, counters->dwsize)))
FPrintf(StdErr,"Oops! AddPart() failed.\n");
if (debug)
Printf("complete path: %s\n",(LONG)counters->out);
/* we don't need the old lock any more */
tmplock = 0;
/* state machine for block type */
switch (BHeader->type) {
case ID_UDIR: /* directory */
/* create directory, set all attribs. */
counters->dircount++;
if (logFile)
FPrintf(logFile, "UDIR %s\n", (LONG)counters->out);
if (!(tmplock = CreateDir(counters->out))) {
FPrintf(StdErr,"CreateDir(\"%s\") failed;", (LONG)counters->out);
if (ERROR_OBJECT_EXISTS != IoErr()) {
FPrintf(StdErr, "\n");
return FALSE;
}
FPrintf(StdErr," object already exists, ignoring...\n");
}
/* set protection, date and filenote */
SetComment(counters->out, ((STDir *)BHeader)->body.filenote);
SetProtection(counters->out, ((STDir *)BHeader)->body.protect);
SetFileDate(counters->out, &((STDir *)BHeader)->body.date);
break;
case ID_FILE: /* file */
/*
* make a lock on parent object, which should be a directory. open file for
* writing, push all data into it, close it. Then set protection, filenote and date.
* That's all. ;)
*/
counters->filecount++;
if (debug)
Printf("opening file %s\n", (LONG)counters->out);
if (logFile)
FPrintf(logFile, "FILE %s\n", (LONG)counters->out);
if (tmplock = Lock(counters->out, ACCESS_READ)) {
UnLock(tmplock);
counters->out = strcat(counters->out, "_1");
}
tmplock = Open(counters->out, MODE_NEWFILE);
if (tmplock)
writeFile(infile, tmplock, (ULONG)BHeader->size);
else {
PrintFault(IoErr(),"Error ");
Close(tmplock);
tmplock = 0;
return FALSE;
}
Close(tmplock);
tmplock = 0;
if (debug)
Printf("Finished writing\n");
SetComment(counters->out, ((STDir *)BHeader)->body.filenote);
SetProtection(counters->out, ((STDir *)BHeader)->body.protect);
SetFileDate(counters->out, &((STDir *)BHeader)->body.date);
break;
case ID_FLNK: /* hardlink to file */
case ID_DLNK: /* hardlink to dir */
counters->linkcount++;
if (logFile)
FPrintf(logFile, "LINK %s\n", (LONG)counters->out);
if (!(tmplock = Lock(Pathlist[indexID(((STHLink*)BHeader)->link)],ACCESS_READ)))
FPrintf(StdErr,"Could not lock file %s to link to\n",
(LONG)Pathlist[indexID(((STHLink*)BHeader)->link)]);
if (tmplock)
if (!MakeLink(counters->out, (LONG)tmplock, FALSE))
FPrintf(StdErr,"MakeLink() failed for hardlink %s\n",
(LONG)counters->out);
SetComment(counters->out, ((STDir *)BHeader)->body.filenote);
SetProtection(counters->out, ((STDir *)BHeader)->body.protect);
SetFileDate(counters->out, &((STDir *)BHeader)->body.date);
break;
case ID_SLNK: /* softlink */
counters->linkcount++;
if (logFile)
FPrintf(logFile, "SLNK %s\n", (LONG)counters->out);
if (!MakeLink(counters->out, (LONG)((STSLink*)BHeader)->link, TRUE))
FPrintf(StdErr,"MakeLink() failed for softlink %s\n",
(LONG)counters->out);
tmplock = 0;
break;
case ID_ERRS: /* huh? */
counters->errcount++;
FPrintf(StdErr,"Found error block. I don't know how to handle it.\n");
break;
case SET_DEL: /* error, may be deleted? */
FPrintf(StdErr,"The file %s was faulty, according to DiskSalv.\n",
(LONG)Pathlist[counters->objectcount - 1]);
break;
default:
FPrintf(StdErr,"Unknown block encountered. Archive format ok?\n");
break;
}
UnLock(tmplock);
} while (BHeader->type !=ID_ENDA);
Printf("%ld files, %ld directories and %ld links restored.\n%ld errors encountered.\n",
counters->filecount,
counters->dircount,
counters->linkcount,
counters->errcount);
return TRUE;
}
BOOL writeFile(BPTR source, BPTR target, LONG size)
{
char *tbuff, *sbuff;
ULONG bcount, i;
/* optimize... read all and/or write all in one go... done */
if (size == 0)
return TRUE;
/*
* allocate all necessary memory, 2 buffers for source and target
*/
bcount = size / STDATASIZE + (size % STDATASIZE ? 1 : 0);
if (debug)
Printf("%ld blocks of memory, %ld byte together, available %ld.\n", bcount, size, AvailMem(MEMF_FAST|MEMF_LARGEST));
if (tbuff = AllocVec(bcount * STDATASIZE, 0L)) {
if (sbuff = AllocVec(bcount * STBLOCKSIZE, 0L)) {
if (bcount * STBLOCKSIZE != Read(source, sbuff, bcount * STBLOCKSIZE))
PutStr("Short read\n");
for (i = 0; i < bcount; i++)
if (((STHeader *)(sbuff + i * STBLOCKSIZE))->type != ID_DATA) { /* find wrong counts */
if (debug)
Printf("Seek %ld bytes, current pos %ld\n", -((LONG)(bcount - i))*STBLOCKSIZE, Seek(source, 0, OFFSET_CURRENT));
Seek(source, -(LONG)((bcount - i)*STBLOCKSIZE), OFFSET_CURRENT); /* set to start of non-data */
break;
}
for (i = 0; i < bcount; i++) { /* copy all data to the compact databuffer */
CopyMem(sbuff + STBLOCKSIZE*i + sizeof(STHeader), tbuff + STDATASIZE*i, STDATASIZE);
}
if (size != Write(target, tbuff, size)) {
PutStr("Short write\n"); /* TODO: there should be more error handling */
}
FreeVec(sbuff);
} else {
FPrintf(StdErr,"source buffer of %ld failed\n", bcount*STBLOCKSIZE);
}
FreeVec(tbuff);
} else {
FPrintf(StdErr,"target buffer of %ld failed\n", size);
}
return TRUE;
}